[PB Generic表][Go SDK]查询单条数据
1. 接口说明
从数据库表中查询一条记录
2. 版本要求
无特殊要求,所有版本都提供了该接口。
3. 准备工作
参见准备工作文档,完成使用该接口前的准备工作,并创建如下PB Generic表。game_players表
syntax = "proto3"; // Specify the version of the protocol buffers language
package tcaplusservice;
import "tcaplusservice.optionv1.proto"; // Use the public definitions of TcaplusDB by importing them.
message game_players { // Define a TcaplusDB table with message
// Specify the primary keys with the option tcaplusservice.tcaplus_primary_key
// The primary key of a TcaplusDB table has a limit of 4 fields
option(tcaplusservice.tcaplus_primary_key) = "player_id, player_name, player_email";
// Specify the primary key indexes with the option tcaplusservice.tcaplus_index
option(tcaplusservice.tcaplus_index) = "index_1(player_id, player_name)";
option(tcaplusservice.tcaplus_index) = "index_2(player_id, player_email)";
// Value Types supported by TcaplusDB
// int32, int64, uint32, uint64, sint32, sint64, bool, fixed64, sfixed64, double, fixed32, sfixed32, float, string, bytes
// Nested Types Message
// primary key fields
int64 player_id = 1;
string player_name = 2;
string player_email = 3;
// Ordinary fields
int32 game_server_id = 4;
repeated string login_timestamp = 5;
repeated string logout_timestamp = 6;
bool is_online = 7;
payment pay = 8;
}
message payment {
int64 pay_id = 1;
uint64 amount = 2;
int64 method = 3;
}
准备工作完成后,将会获得以下信息,这些信息在使用SDK时会被用到:
- 目录服务器地址列表
- 业务ID
- 业务访问密码
- 游戏区ID
- 数据表名
4. 示例代码
示例代码的基本执行过程:
- 定义表配置参数
- 设置日志配置;
- 创建客户端;
- 发送请求并处理响应;
- 销毁客户端。
客户端初始化示例:
package main
import (
"fmt"
"git.woa.com/gcloud_storage_group/tcaplus-go-api"
)
// 定义表配置参数
const (
AppId = uint64(2)
ZoneId = uint32(3)
DirUrl = "tcp://x.x.x.x:xxxx"
Signature = "xxxxxxxxxxxxx"
TableName = "game_players"
)
var client *tcaplus.PBClient
func main() {
// 创建客户端
client = tcaplus.NewPBClient()
// 设置日志配置,logconf.xml文件设置了日志级别
if err := client.SetLogCfg("./logconf.xml"); err != nil {
fmt.Println(err.Error())
return
}
//构造Map对象存储对应表格组下所有的表
zoneList := []uint32{ZoneId}
zoneTable := make(map[uint32][]string)
zoneTable[ZoneId] = []string{TableName}
// 连接TcaplusDB后端
err := client.Dial(AppId, zoneList, DirUrl, Signature, 30, zoneTable)
if err != nil {
fmt.Printf("init failed %v\n", err.Error())
return
}
// 设置默认使用的zone
client.SetDefaultZoneId(ZoneId)
fmt.Printf("Dial finish\n")
getExample()
// 程序退出时调用Close销毁客户端
client.Close()
}
4.1 同步调用示例
同步调用编码最简单,通过多协程并发 示例目录
package main
import (
"fmt"
"git.woa.com/gcloud_storage_group/tcaplus-go-api/example/PB/table/tcaplusservice"
"git.woa.com/gcloud_storage_group/tcaplus-go-api/example/PB/tools"
"git.woa.com/gcloud_storage_group/tcaplus-go-api/logger"
"git.woa.com/gcloud_storage_group/tcaplus-go-api/protocol/option"
)
func getExample() {
// 设置msg的key字段
msg := &tcaplusservice.GamePlayers{
PlayerId: 10805514,
PlayerName: "Calvin",
PlayerEmail: "calvin@test.com",
}
// 同步发送Get请求,响应消息会覆盖msg
opt := &option.PBOpt{}
err := client.DoGet(msg, opt)
if err != nil {
logger.ERR("SendRequest error:%s", err)
return
}
fmt.Println(tools.ConvertToJson(msg))
fmt.Println(opt.Version) //记录版本
}
4.2 同步调用示例2
该示例同步调用方式比较类似C++接口的调用方式,需要创建请求和解析响应示例目录
package main
import (
"fmt"
"github.com/tencentyun/tcaplusdb-go-sdk/tdr/example/PB/table/tcaplusservice"
"github.com/tencentyun/tcaplusdb-go-sdk/tdr/example/PB/tools"
"github.com/tencentyun/tcaplusdb-go-sdk/tdr/logger"
"github.com/tencentyun/tcaplusdb-go-sdk/tdr/protocol/cmd"
"github.com/tencentyun/tcaplusdb-go-sdk/tdr/terror"
"time"
)
func getExample() {
// 生成 get 请求
req, err := client.NewRequest(ZoneId, "game_players", cmd.TcaplusApiGetReq)
if err != nil {
logger.ERR("NewRequest error:%s", err)
return
}
// 向请求中添加记录,对于 generic 表 index 无意义,填 0 即可
record, err := req.AddRecord(0)
if err != nil {
logger.ERR("AddRecord error:%s", err)
return
}
// 向记录中填充数据
// key 字段必填,通过 proto 文件设置 key
// 本例中为 option(tcaplusservice.tcaplus_primary_key) = "player_id, player_name, player_email";
msg := &tcaplusservice.GamePlayers{
PlayerId: 10805514,
PlayerName: "Calvin",
PlayerEmail: "calvin@test.com",
}
_, err = record.SetPBData(msg)
if err != nil {
logger.ERR("SetPBData error:%s", err)
return
}
// 发送请求,接收响应
resp, err := client.Do(req, 5*time.Second)
if err != nil {
logger.ERR("SendRequest error:%s", err)
return
}
// 获取响应结果
errCode := resp.GetResult()
if errCode != terror.GEN_ERR_SUC {
logger.ERR("insert error:%s", terror.GetErrMsg(errCode))
return
}
// 如果有返回记录则用以下接口进行获取
for i := 0; i < resp.GetRecordCount(); i++ {
record, err := resp.FetchRecord()
if err != nil {
logger.ERR("FetchRecord failed %s", err.Error())
return
}
newMsg := &tcaplusservice.GamePlayers{}
err = record.GetPBData(newMsg)
if err != nil {
logger.ERR("GetPBData failed %s", err.Error())
return
}
fmt.Println(tools.ConvertToJson(newMsg))
}
}
4.2 异步调用示例
异步发送可以使用较少的协程实现较大的并发,编码相对复杂,示例目录
package main
import (
"fmt"
"git.woa.com/gcloud_storage_group/tcaplus-go-api/example/PB/table/tcaplusservice"
"git.woa.com/gcloud_storage_group/tcaplus-go-api/example/PB/tools"
"git.woa.com/gcloud_storage_group/tcaplus-go-api/logger"
"git.woa.com/gcloud_storage_group/tcaplus-go-api/protocol/cmd"
"git.woa.com/gcloud_storage_group/tcaplus-go-api/terror"
"sync"
"time"
)
func getExample() {
wg := sync.WaitGroup{}
wg.Add(1)
// 在另一协程处理响应消息
go func() {
defer wg.Done()
for {
// resp err 均为 nil 说明响应池中没有任何响应
resp, err := client.RecvResponse()
if err != nil {
logger.ERR("RecvResponse error:%s", err)
continue
} else if resp == nil {
time.Sleep(time.Microsecond * 5)
continue
}
// 获取响应结果
errCode := resp.GetResult()
if errCode != terror.GEN_ERR_SUC {
logger.ERR("insert error:%s", terror.GetErrMsg(errCode))
return
}
// 如果有返回记录则用以下接口进行获取
for i := 0; i < resp.GetRecordCount(); i++ {
record, err := resp.FetchRecord()
if err != nil {
logger.ERR("FetchRecord failed %s", err.Error())
return
}
newMsg := &tcaplusservice.GamePlayers{}
err = record.GetPBData(newMsg)
if err != nil {
logger.ERR("GetPBData failed %s", err.Error())
return
}
fmt.Println(tools.ConvertToJson(newMsg))
}
}
}()
// 生成 get 请求
req, err := client.NewRequest(tools.ZoneId, "game_players", cmd.TcaplusApiGetReq)
if err != nil {
logger.ERR("NewRequest error:%s", err)
return
}
// 向请求中添加记录,对于 generic 表 index 无意义,填 0 即可
record, err := req.AddRecord(0)
if err != nil {
logger.ERR("AddRecord error:%s", err)
return
}
// 向记录中填充数据
// key 字段必填,通过 proto 文件设置 key
// 本例中为 option(tcaplusservice.tcaplus_primary_key) = "player_id, player_name, player_email";
msg := &tcaplusservice.GamePlayers{
PlayerId: 10805514,
PlayerName: "Calvin",
PlayerEmail: "calvin@test.com",
}
_, err = record.SetPBData(msg)
if err != nil {
logger.ERR("SetPBData error:%s", err)
return
}
// 异步发送请求
if err := client.SendRequest(req); err != nil {
fmt.Printf("SendRequest failed %v\n", err.Error())
return
}
wg.Wait()
}
5. 请求对象(request)中的方法说明
注:此处未列出请求对象的其它方法,即表示该方法在查询数据的场景不需要使用,误用可能会导致报错。
/**
@brief 向请求中添加一条记录。
@param [IN] index 用于List操作,通常>=0,表示该Record在所属List中的Index;
对于Generic操作,index无意义,设0即可
@retval record.Record 返回记录指针
@retval error 错误码
**/
AddRecord(index int32) (*record.Record, error)
/**
@brief 设置请求的异步事务ID,api会将其值不变地通过对应的响应消息带回来
@param [IN] asyncId 请求对应的异步事务ID
**/
SetAsyncId(id uint64)
/**
@brief 设置请求的通用标志位,可以通过"按位或"操作同时设定多个值
@param [IN] flag. 请求标志位的值
@retval 0 设置成功
@retval <0 失败,返回对应的错误码。通常因为未初始化。
@note 有效的标志位包括:
* TCAPLUS_FLAG_FETCH_ONLY_IF_MODIFIED:
* "数据变更才取回"标志位。在发起读操作之前,用户代码通过 TcaplusServiceRecord::SetVersion()
* 带上本地缓存数据的版本号,并将此标志置位,那么存储端检测到当前数据与API本地缓存的数据版本
* 一致时,表明该记录未发生过修改,API缓存的数据是最新的,因此在响应中将不会携带实际的数据,
* 只是返回 TcapErrCode::COMMON_INFO_DATA_NOT_MODIFIED 的错误码
*
* 在请求中设置了此标志位之后,收到响应后应首先通过 TcaplusServiceResponse::GetFlags() 来获知
* 发送请求时是否设置了TCAPLUS_FLAG_FETCH_ONLY_IF_MODIFIED标志.
*
* 只有如下请求支持设置此标志:
* TCAPLUS_API_GET_REQ,
* TCAPLUS_API_LIST_GET_REQ,
* TCAPLUS_API_LIST_GETALL_REQ
*
* TCAPLUS_FLAG_FETCH_ONLY_IF_EXPIRED:
* "数据过期才取回"标志位。在发起读操作之前,用户代码通过 SetExpireTime() 设定数据过期时间,
* 并将此标志置位,那么存储端若检测到记录在指定时间内发生过更新,则将数据返回,
* 否则不返回实际数据,只是返回 TcapErrCode::COMMON_INFO_DATA_NOT_MODIFIED 的错误码。
*
* 在请求中设置了此标志位之后,收到响应后应首先通过 TcaplusServiceResponse::GetFlags() 来获知
* 发送请求时是否设置了 TCAPLUS_FLAG_FETCH_ONLY_IF_EXPIRED 标志.
*
* 只有如下请求支持设置此标志:
* TCAPLUS_API_BATCH_GET_REQ
*
* TCAPLUS_FLAG_ONLY_READ_FROM_SLAVE
* 设置此标志后,读请求将会直接发送给Tcapsvr Slave 节点。
* Tcapsvr Slave 通常比较空闲,设置此标志有助于充分利用Tcapsvr Slave 资源。
*
* 适用场景:
* 对于数据实时性要求不高的读请求,
* 包括generic表和list表的所有读请求以及batchget,遍历请求
*
* TCAPLUS_FLAG_LIST_RESERVE_INDEX_HAVING_NO_ELEMENTS
* 设置此标志后,List表删除最后一个元素时需要保留index和version。
* ListDelete ListDeleteBatch ListDeleteAll操作在删除list表最后一个元素时,
* 设置此标志在写入新的List记录时,版本号依次增长,不会被重置为1。
*
* 适用场景:
* 业务需要确定某个表在删除最后一个元素时是否需要保留index和version
* 主要涉及List表的使用体验
*
*/
SetFlags(flag int32) int
/**
@brief 清理请求的通用标志位,可以通过"按位或"操作同时设定多个值
@param [IN] flag. 请求标志位的值
@retval 0 设置成功
@retval <0 失败,返回对应的错误码。通常因为未初始化。
@note 有效的标志位列表及详细解释请参考 SetFlags()
*/
ClearFlags(flag int32) int
/**
@brief 获取请求的通用标志位
@return 返回请求的通用标志位
@note 有效的标志位列表及详细解释请参考 SetFlags()
*/
GetFlags() int32
/**
@brief 设置需要查询或更新的Value字段名称列表,即部分Value字段查询和更新,可用于get、replace、update操作。
@param [IN] valueNameList 需要查询或更新的字段名称列表
@retval error 错误码
@note 在使用该函数设置字段名时,字段名只能包含value字段名,不能包含key字段名;对于数组类型的字段,
refer字段和数组字段要同时设置或者同时不设置,否则容易数据错乱
**/
SetFieldNames(valueNameList []string) error
/**
@brief 设置用户缓存,响应消息将携带返回
@param [IN] userBuffer 用户缓存
@retval error 错误码
**/
SetUserBuff(userBuffer []byte) error
6. 响应对象(response)中的方法说明
注:此处未列出的响应对象的其它方法,即表示该方法在查询数据的场景不需要使用,误用可能会导致报错。
/*
@brief 获取响应结果
@retval int tcaplus api自定义错误码。 0,表示请求成功;非0,有错误码,可从terror.GetErrMsg(int)得到错误消息
*/
GetResult() int
/*
@brief 获取响应表名
@retval string 响应消息对应的表名称
*/
GetTableName() string
/*
@brief 获取响应appId
@retval uint64 响应消息对应的appId
*/
GetAppId() uint64
/*
@brief 获取响应zoneId
@retval uint32 响应消息对应的zoneId
*/
GetZoneId() uint32
/*
@brief 获取响应命令
@retval int 响应消息命令字,cmd包中的响应命令字
*/
GetCmd() int
/*
@brief 获取响应异步id,和请求对应
@retval uint64 响应消息对应的异步id和请求对应
*/
GetAsyncId() uint64
/*
@brief 获取本响应中结果记录条数
@retval int 响应中结果记录条数
*/
GetRecordCount() int
/*
@brief 从结果中获取一条记录
@retval *record.Record 记录指针
@retval error 错误码
*/
FetchRecord() (*record.Record, error)
/*
@brief 获取响应消息中的用户缓存信息
@retval []byte 用户缓存二进制,和请求消息中的buffer内容一致
*/
GetUserBuffer() []byte
7. 记录对象(record)中的方法说明
注:此处未列出的响应对象的其它方法,即表示该方法在查询数据的场景不需要使用,误用可能会导致报错。
- record的通用方法:
/**
@brief 获取记录版本号
@retval 记录版本号
@note 对于Generic操作表示获取Record的版本;对于List操作表示获取Record所在List的版本。
**/
func (r *Record) GetVersion() int32
/**
@brief 基于 PB Message 设置record数据
@param [IN] data PB Message
@retval []byte 记录的keybuf,用来唯一确定一条记录,多用于请求与响应记录相对应
@retval error 错误码
*/
func (r *Record) SetPBData(message proto.Message) ([]byte, error)
/**
@brief 基于 PB Message 读取record数据
@param [IN] data PB Message
@retval []byte 记录的keybuf,用来唯一确定一条记录,多用于请求与响应记录相对应
@retval error 错误码
**/
func (r *Record) GetPBData(message proto.Message) error
8. 常见问题
详见错误码含义和处理方法。